package com.bensoft.minesweeper.resources;

import com.bensoft.minesweeper.gui.preferences.ApplicationPreferences;
import com.bensoft.minesweeper.resources.strings.StringResources;
import java.applet.Applet;
import java.applet.AudioClip;
import java.awt.Color;
import java.awt.Image;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EventListener;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import javax.swing.Icon;
import javax.swing.ImageIcon;

/**
 *
 * A class used to access application wide image and sound resources. Image and
 * sound credit goes to my cousin for the most part, though I worked on them a
 * little. The score numbers (dig*.png) are possibly a little too close to the
 * Microsoft's ones than is legal.
 *
 * @author Ben Wolsieffer
 */
@SuppressWarnings("StaticNonFinalUsedInInitialization")
public abstract class FileResources {

    private static ImageIcon mineIcon,
            mineXIcon,
            questionIcon,
            flagIcon,
            mineAppIcon,
            ohSmile,
            clickedSmile,
            smile,
            deadSmile,
            sunSmile;
    private static ImageIcon[] numberIcons = new ImageIcon[8];
    private static ImageIcon[] digitIcons = new ImageIcon[10];
    private static ImageIcon negDigit, blankDigit;
    private static AudioClip bopClip, bangClip, bingClip;
    private static Properties themes;
    private static final String defaultThemeName = "Modern";
    private static final String defaultThemeBase = "modern/";
    private static String themeName = defaultThemeName;
    private static String themeBase = defaultThemeBase;
    private static ArrayList<ThemeChangeListener> themeChangeListeners = new ArrayList<>();
    private static Properties defaultColors = new Properties();
    private static Properties colors = new Properties(defaultColors);

    static {
        try (FileInputStream fis = new FileInputStream(new File(FileResources.class.getResource("themes.properties").toURI()))) {
            themes = new Properties();
            themes.load(fis);
            setTheme(ApplicationPreferences.getTheme());
        } catch (IOException | URISyntaxException ex) {
            themes = null;
            System.err.println(StringResources.getString("error.unabletoloadthemes"));
        }
        loadResources();
    }

    private static void loadResources() {
        try {
            mineIcon = new ImageIcon(FileResources.class.getResource(themeBase + "mine.png"));
            mineXIcon = new ImageIcon(FileResources.class.getResource(themeBase + "mineX.png"));
            questionIcon = new ImageIcon(FileResources.class.getResource(themeBase + "question.png"));
            flagIcon = new ImageIcon(FileResources.class.getResource(themeBase + "flag.png"));
            negDigit = new ImageIcon(FileResources.class.getResource(themeBase + "neg.png"));
            blankDigit = new ImageIcon(FileResources.class.getResource(themeBase + "digBlank.png"));
            mineAppIcon = new ImageIcon(FileResources.class.getResource(themeBase + "minebig.png"));
            ohSmile = new ImageIcon(FileResources.class.getResource(themeBase + "ohSmile.png"));
            smile = new ImageIcon(FileResources.class.getResource(themeBase + "smile.png"));
            sunSmile = new ImageIcon(FileResources.class.getResource(themeBase + "sunSmile.png"));
            deadSmile = new ImageIcon(FileResources.class.getResource(themeBase + "deadSmile.png"));
            clickedSmile = new ImageIcon(FileResources.class.getResource(themeBase + "clickedSmile.png"));
            for (int i = 0; i < numberIcons.length; i++) {
                numberIcons[i] = new ImageIcon(FileResources.class.getResource(themeBase + (i + 1) + ".png"));
            }
            for (int i = 0; i < digitIcons.length; i++) {
                digitIcons[i] = new ImageIcon(FileResources.class.getResource(themeBase + "dig" + i + ".png"));
            }
            bopClip = Applet.newAudioClip(FileResources.class.getResource(themeBase + "bop.wav"));
            bangClip = Applet.newAudioClip(FileResources.class.getResource(themeBase + "bang.wav"));
            bingClip = Applet.newAudioClip(FileResources.class.getResource(themeBase + "bing.wav"));
            try (InputStream colorStream = FileResources.class.getResourceAsStream(themeBase + "colors.properties")) {
                colors.load(colorStream);
            } catch (IOException ex) {
                System.err.println(StringResources.getString("error.unabletoloadcolors"));
            }
        } catch (NullPointerException ex) {
            System.err.println(StringResources.getString("error.unabletoloadresources"));
        }
    }

    public static Color getColor(String key) {
        String value = colors.getProperty(key);
        if (value == null) {
            return null;
        }
        try {
            return Color.decode(value);
        } catch (NumberFormatException ex) {
            try {
                Field f = Color.class.getField(value);
                if (Modifier.isStatic(f.getModifiers()) && f.getType().equals(Color.class)) {
                    return (Color) f.get(null);
                }
            } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex1) {
            }
        }
        return null;
    }

    public static Icon getMineIcon() {
        return mineIcon;
    }

    public static Icon getMineXIcon() {
        return mineXIcon;
    }

    public static Icon getQuestionIcon() {
        return questionIcon;
    }

    public static Icon getFlagIcon() {
        return flagIcon;
    }

    public static Image getMineImage() {
        return mineIcon.getImage();
    }

    public static Image getMineXImage() {
        return mineXIcon.getImage();
    }

    public static Image getQuestionImage() {
        return questionIcon.getImage();
    }

    public static Image getFlagImage() {
        return flagIcon.getImage();
    }

    public static Icon getNumberIcon(int n) {
        if (n > 8 || n < 1) {
            return null;
        }
        return numberIcons[n - 1];
    }

    public static Image getNumberImage(int n) {
        if (n > 8 || n < 1) {
            return null;
        }
        return numberIcons[n - 1].getImage();
    }

    public static Image getDigitImage(int n) {
        if (n > 9 || n < 0) {
            return null;
        }
        return digitIcons[n].getImage();
    }

    public static Icon getDigitIcon(int n) {
        if (n > 9 || n < 0) {
            return null;
        }
        return digitIcons[n];
    }

    public static Image getNegDigitImage() {
        return negDigit.getImage();
    }

    public static Icon getNegDigitIcon() {
        return negDigit;
    }

    public static Icon getBlankDigitIcon() {
        return blankDigit;
    }

    public static Image getBlankDigitImage() {
        return blankDigit.getImage();
    }

    public static void playTimerBop() {
        if (ApplicationPreferences.areSoundsOn()) {
            bopClip.play();
        }
    }

    public static void playExplosion() {
        if (ApplicationPreferences.areSoundsOn()) {
            bangClip.play();
        }
    }

    public static void playBing() {
        if (ApplicationPreferences.areSoundsOn()) {
            bingClip.play();
        }
    }

    public static Image getAppImage() {
        return mineAppIcon.getImage();
    }

    public static Icon getAppIcon() {
        return mineAppIcon;
    }

    public static Icon getOhSmileIcon() {
        return ohSmile;
    }

    public static Icon getClickedSmileIcon() {
        return clickedSmile;
    }

    public static Icon getSmileIcon() {
        return smile;
    }

    public static Icon getDeadSmileIcon() {
        return deadSmile;
    }

    public static Icon getSunSmileIcon() {
        return sunSmile;
    }

    public static Image getSmileImage() {
        return smile.getImage();
    }

    public static Image getDeadSmileImage() {
        return deadSmile.getImage();
    }

    public static Image getClickedSmileImage() {
        return clickedSmile.getImage();
    }

    public static Image getOhSmileImage() {
        return ohSmile.getImage();
    }

    public static Image getSunSmileImage() {
        return sunSmile.getImage();
    }

    public static String getCurrentTheme() {
        return themeName;
    }

    public static Set<String> getThemes() {
        if (themes != null) {
            return themes.stringPropertyNames();
        }
        // Hard coded one item set that is returned if the themes aren't working
        return new Set() {
            @Override
            public int size() {
                return 1;
            }

            @Override
            public boolean isEmpty() {
                return false;
            }

            @Override
            public boolean contains(Object o) {
                return o.equals(defaultThemeName);
            }

            @Override
            public Iterator iterator() {
                return new Iterator() {
                    boolean i = true;

                    @Override
                    public boolean hasNext() {
                        return i;
                    }

                    @Override
                    public Object next() {
                        i = false;
                        return defaultThemeName;
                    }

                    @Override
                    public void remove() {
                        throw new UnsupportedOperationException("Removal is not allowed");
                    }
                };
            }

            @Override
            public Object[] toArray() {
                return new String[]{defaultThemeName};
            }

            @Override
            public Object[] toArray(Object[] a) {
                if (a.length > 0) {
                    a[0] = defaultThemeName;
                }
                return a;
            }

            @Override
            public boolean add(Object e) {
                throw new UnsupportedOperationException("Adding is not allowed");
            }

            @Override
            public boolean remove(Object o) {
                throw new UnsupportedOperationException("Removal is not allowed");
            }

            @Override
            public boolean containsAll(Collection c) {
                return (c.size() == 1 && c.iterator().next().equals(defaultThemeName));
            }

            @Override
            public boolean addAll(Collection c) {
                throw new UnsupportedOperationException("Adding is not allowed");
            }

            @Override
            public boolean retainAll(Collection c) {
                throw new UnsupportedOperationException("Not allowed");
            }

            @Override
            public boolean removeAll(Collection c) {
                throw new UnsupportedOperationException("Removal is not allowed");
            }

            @Override
            public void clear() {
                throw new UnsupportedOperationException("Clearing is not allowed");
            }
        };
    }

    public static void setTheme(String key) {
        String t = null;
        if (themes != null) {
            t = themes.getProperty(key);
        } else if (key.equals(defaultThemeName)) {
            t = defaultThemeBase;
        }
        if (t != null) {
            themeBase = t;
            themeName = key;
            ApplicationPreferences.setTheme(key);
            loadResources();
            fireThemeChange();
        }
    }

    public static interface ThemeChangeListener extends EventListener {

        /**
         * Called when the theme changes.
         */
        public void themeChanged();
    }

    private static void fireThemeChange() {
        for (ThemeChangeListener l : themeChangeListeners) {
            l.themeChanged();
        }
    }

    public static void addThemeChangeListener(ThemeChangeListener listener) {
        themeChangeListeners.add(listener);
        listener.themeChanged();
    }

    public static void removeThemeChangeListener(ThemeChangeListener listener) {
        themeChangeListeners.remove(listener);
    }
}